#include "strutil.hpp"

StrUtil::StrUtil(const char *head,
				 char sep,
				 const char *tail,
				 uint8_t *pbuf,
				 uint8_t bufSize,
				 uint8_t *SrcAddress,
				 uint8_t SrcSize)
{
	this->phead = (uint8_t *)head;
    this->headSize = strlen(head);
    this->separator = sep;
    this->ptail = (uint8_t *)tail;
    this->tailSize = strlen(tail);
    this->pBuf = (uint8_t *)pbuf;
    this->BufSize = bufSize;
    this->pSrcAdr = (uint8_t *)SrcAddress;
    this->SrcAdrSize = SrcSize ;
}

/**
  * @brief  将一个缓存中的字符串分割为多个子串
  * @param	str :	要分割的字符串缓存
  * @param	size:   字符串缓存的尺寸
  * @note   请注意该函数会更改字符串缓存，不能传入const char*
  * @note	请先checkSplitCondition检查再调用该函数
  * @retval 解析出的数据个数
  */
uint8_t StrUtil::split(uint8_t *str, uint8_t size)
{
	uint8_t *pstr = str, *pbuf = this->pBuf;
	uint8_t index = 0;
	if(str == NULL || size == 0 || this->headPos < 0 || this->tailPos < 0) return 0;
	str[this->tailPos] = '\0';//帧尾改成结束符
	pstr += this->headPos + this->headSize;//偏移到第一个数据的位置
	if(*pstr != this->separator)
		pbuf[index++] = (pstr - str);//记录第一个数据
	else goto reCheck;
	while(this->tailPos > (pstr - str))
	{
		if(*pstr == this->separator)//检测分隔符检测
		{
			reCheck:
			*pstr = '\0';//把分隔符换为结束符
			pstr++;
			if(*pstr == this->separator) goto reCheck;
			else if(index < this->BufSize && this->tailPos > (pstr - str))//缓存没满
			{
				pbuf[index++] = (pstr - str);
				// printf("split index = %d\n",pstr - str);
			}
		}
		pstr++;
	}
	this->BufUsage = index;//将分隔符的数量存起来
	// printf("split end Usage = %d\n",this->BufUsage);
	return index;//结束匹配

}


/**
 * @brief     设置待分析数据的地址
 * @param 	  资源首地址
 * @param	  尺寸
 * @note 	  需要检测用split解析多个地址时使用该函数更
 * 			  改分析的地址。切换后当前结果丢失需要重新分析
 * @retval    None
**/
void StrUtil::setSrcAddress(uint8_t* Address, uint8_t Size)
{
	if(Address == NULL || Size == 0) return;
	this->pSrcAdr = Address;
	this->SrcAdrSize = Size;
	memset(this->pBuf, 0 , this->BufSize);
	this->BufUsage = 0;
}

/**
 * @brief     在缓存中查找数据段的位置
 * @param 	  pdata	查找的缓存
 * @param 	  Size	缓存长度
 * @param 	  AimData	搜索的字节串目标
 * @param 	  AimSize	目标的长度
 * @retval    [>=0:帧头偏移值][-1:不存在][-2:参数错误]
**/
int StrUtilMemSearch(uint8_t* pdata, uint8_t Size, uint8_t* AimData, uint8_t AimSize)
{
	uint8_t* pch;
	uint8_t* pbuf = pdata;
	if(pdata == NULL || Size == 0 || AimData == NULL || AimSize == 0)return -2;

	while((pbuf < (pdata + Size)))
	{
		pch = (uint8_t*)memchr((char*)pbuf, *AimData, Size - (pbuf - pdata));
		// printf("pch: %c\n", *pch);
		if(pch == NULL)return -1;//没有找到帧头第一个字节
		
		if(AimSize == 1 || memcmp(pch, AimData, AimSize) == 0)break;//匹配帧头成功
		else
			pbuf = pch + 1;//迭代继续查找帧头
		
	}
	// printf("Search end pbuf: %c\n", *pch);
	return pch - pdata;
}

int StrUtil::checkHead(uint8_t* data, uint16_t size)
{
	this->headPos = StrUtilMemSearch(data, size, this->phead, this->headSize);
	return this->headPos;
}
int StrUtil::checkHead(char *str)
{
	this->headPos = StrUtilMemSearch((uint8_t*)str, strlen(str), this->phead, this->headSize);
	return this->headPos;
}
int StrUtil::checkHead(void)
{
	this->headPos = StrUtilMemSearch(this->pSrcAdr, this->SrcAdrSize, this->phead, this->headSize);
	return this->headPos;
}

int StrUtil::checkTail(uint8_t* data, uint16_t size)
{
	this->headPos = StrUtilMemSearch(data, size, this->ptail, this->tailSize);
	return this->tailPos;
}
int StrUtil::checkTail(char *str)
{
	this->tailPos = StrUtilMemSearch((uint8_t*)str, strlen(str), this->ptail, this->tailSize);
	return this->tailPos;
}
int StrUtil::checkTail(void)
{
	this->tailPos = StrUtilMemSearch(this->pSrcAdr, this->SrcAdrSize, this->ptail, this->tailSize);
	return this->tailPos;
}

bool StrUtil::checkSplitCondition(uint8_t* data, uint16_t size)
{
	if (this->checkHead(data, size) >=0 && this->checkTail(data, size)>=0)
		return true;
	return false;
}

bool StrUtil::checkSplitCondition(char *str)
{
	if (this->checkHead(str) >=0 && this->checkTail(str)>=0)
		return true;
	return false;
}

bool StrUtil::checkSplitCondition(void)
{
	if (this->checkHead() >=0 && this->checkTail()>=0)
		return true;
	return false;
}

int StrUtil::parse(uint8_t* data, uint16_t size)
{
	if (this->checkSplitCondition(data, size))
		return this->split(data, size);
	return 0;
}
int StrUtil::parse(char *str)
{
	if (this->checkSplitCondition(str))
		return this->split((uint8_t*)str, strlen(str));
	return 0;
}
int StrUtil::parse(void)
{
	if (this->checkSplitCondition())
		return this->split(this->pSrcAdr, this->SrcAdrSize);
	return 0;
}


/**
 * @brief     获取分割后某段的数据长度
 * @param	  引索
 * @retval    帧数据段的长度
**/
uint16_t StrUtil::getLen(uint8_t index)
{
	return strlen((const char *)&this->pSrcAdr[this->pBuf[index]]);
}

/**
 * @brief     获取分割后的整型数据
 * @param	  引索
 * @retval    整型数据值
**/
int StrUtil::getInt(uint8_t index)
{
	return atol((const char*)(&this->pSrcAdr[this->pBuf[index]]));
}

/**
 * @brief     获取分割后的字符
 * @param	  引索
 * @retval    字符串指针
**/
char* StrUtil::getStr(uint8_t index)
{
	return (char*)(&this->pSrcAdr[this->pBuf[index]]);
}

/**
 * @brief     获取分割后的浮点型数据
 * @param	  引索
 * @retval    浮点数据
**/
double StrUtil::getDouble(uint8_t index)
{
	return atof((const char*)(&this->pSrcAdr[this->pBuf[index]]));
}

/**
 * @brief     获取分割后的原始数据
 * @param	  引索
 * @retval    数据指针(到'\0'结束)
**/
uint8_t* StrUtil::getData(uint8_t index)
{
	return (uint8_t*)(&this->pSrcAdr[this->pBuf[index]]);
}

/**
 * @brief     将分割后的字符型数据对比
 * @param	  引索
 * @param     要比较的字符
 * @retval    [1:相同][0:不同]
**/
bool StrUtil::strCmp(uint8_t index, char *strNeedCmpare)
{
	return (std::strcmp(this->getStr(index),strNeedCmpare) == 0 ? 1 : 0);
}

/**
 * @brief     将分割后的字符型数据对比
 * @param	  引索
 * @param     要比较的字符
 * @retval    [1:相同][0:不同]
**/
bool StrUtil::strCmp(uint8_t index, const char *strNeedCmpare)
{
	return (std::strcmp(this->getStr(index),strNeedCmpare) == 0 ? 1 : 0);
}
/**
用法：
1.实例化一个解析器
	uint8_t DataSU_Buf[10]; // 解析器的缓存,格式化后的数据的引索存储到这里，最大10个
	StrUtil DataSU("Data:",',',";",DataSU_Buf,sizeof(DataSU_Buf),NULL,0);
2.设置需要解析的数据的位置
	DataSU.setSrcAddress(pdata, size);
3.开启一次解析，如果有数据帧会自动分割
	DataSU.parse();
下面是OS的示例任务，串口接收到数据后通过通知该任务来解析数据
void TaskMsgHandle(void const *argument)
{
    const uint32_t xTicksToWait = portMAX_DELAY; // 被系统挂起等待通知的时间
    uint32_t ulNotifiedValue; 	//存储通知的值
    BaseType_t xState; 			//任务获取通知的状态
    uint8_t *pdata; 			//数据指针
    uint32_t size;  			//数据大小
    for (;;)
    {
        // 获取任务通知值并清空
        xState = xTaskNotifyWait( pdFALSE,
								  0xFFFFFFFF,
								  &ulNotifiedValue,
								  xTicksToWait );
        if (xState == pdPASS)
        {
            if (ulNotifiedValue & 0x01) // 来自串口1的数据
            {
                pdata = UART_GetRxData(&hrx1);
                size = UART_GetRxSize(&hrx1);
            }
            DataSU.setSrcAddress(pdata, size);
            if (DataSU.parse()) //解析成功返回true
            {
                //TODO...
            }
        }
    }
}
*/


